<?php
/*---------------------------------------------------------------------------
classe de manipulation de document xml
implementant la selection par xpath

file version 2005-04-12 11:40:30
---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
cette implementation repose sur xmldom
---------------------------------------------------------------------------*/

class xpathDoc {
/*---------------------------------------------------------------------------
                                                         proprietes publiques
---------------------------------------------------------------------------*/
	var $template; // document par defaut
	//var $encoding="UTF-8";
	var $encoding="UTF-8";
	//var $authorizedCharset=' _0123456789abcdefghijklmnopqrstuvwxyzéèçàâäêëîïùûüABCDEFGHIJKLMNOPQRSTUVWXYZ <>&?"#\'([-|?\\?@)]=+}{?,;.:/!%*$???';
/*---------------------------------------------------------------------------
                                                                 constructeur
---------------------------------------------------------------------------*/
	function xpathdoc($input=NULL, $encoding="") {
		// verification de la presence du module domxml
		if (!function_exists("domxml_open_mem")) {
			trigger_error("cette implementation s'appuie sur domxml, qui ne semble pas etre installe.",E_USER_WARNING);
			return false;
		}
		$this->encoding=$encoding;
		// selon le type de l'input, chargement fichier, chaine ou du template
		if ($input===NULL) {
			$xml=$this->template;
		} else if (@is_file($input)===true) {
			$xml=join('',file($input));
		} else if (is_string($input)) {
			$xml=$input;
			//$xml=$this->_applyToInput($xml);
		} else {
			$this->_error('xml doit être une chaine',E_USER_NOTICE);
			$xml=$this->template;
		}
		//echo htmlentities($xml);
		if ($encoding=$this->_findEncoding($xml)) $this->setEncoding($encoding);
		//echo $encoding.$this->encoding;
		if (!$this->dom=$this->_makeDom($xml)) {
			trigger_error("impossible de charger le nouveau doc :",E_USER_WARNING);
			echo '<pre>'.htmlentities($xml).'</pre>';
			return false;
		}
		// definition du doc comme contexte xpath
		if (!$this->xpathContext=xpath_new_context($this->dom)) {
			trigger_error("chargement : impossible de definir le contexte xpath !",E_USER_WARNING);
			return false;
			}
		//$this->log=array();
		
		// definition des entites
		//$this->entities=$this->dom->entities();
	}
/*---------------------------------------------------------------------------
                                                           methodes publiques
---------------------------------------------------------------------------*/
	// retourne le ou les noeuds à l'emplacement indiqué
	function get($xpath="/") {
		if ($xpath=="/") return $this->getAll();
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$selectedXml="";
		foreach($xpathResult->nodeset as $node) {
			// est ce un attribut ?
			if (get_class($node)=="domattribute") {
				$content=" ".$node->name()."=".$node->value()." ";
				//$content=$node->dump_mem(true,$this->dom->encoding);
			} else { // il s'agit bien d'un noeud
				$content=$this->_dump_node_with_encoding($node,$this->encoding);
			}
			$selectedXml.=$content;
		}
		return $selectedXml;
	}
	// retourne les noeuds selectionnées sous forme de tableau
	function getAsArray($xpath="/") {
		if ($xpath=="/") return $this->getAll();
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$result=array();
		foreach($xpathResult->nodeset as $node) {
			// est ce un attribut ?
			if (get_class($node)=="domattribute") {
				$content=" ".$node->name()."=".$node->value()." ";
				//$content=$node->dump_mem(true,$this->dom->encoding);
			} else { // il s'agit bien d'un noeud
				$content=$this->_dump_node_with_encoding($node,$this->encoding);
			}
			$result[]=$content;
		}
		return $result;
	}
	// retourne le contenu integral du document
	function getAll($indent=0) {
		return $this->dom->dump_mem($indent, $this->encoding);
	}
	function getAllShort($indent=0) {
		$xml=$this->getAll($indent);
		$xml=eregi_replace('<?xml([a-zA-Z0-9 _-]*)?'.'>','',$xml);
		return $xml;
	}
	function getTypes($xpath='/') {
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$types=array();
		foreach($xpathResult->nodeset as $node) {
			$types[]=$node->node_type();
		}
		return join(' ',$types);
	}
	// alias de get et getall
	function select($xpath="/") { return $this->get($xpath); }
	function selectAll() { return $this->getAll(); }
	// retourne le contenu du noeud ou de l'attribut
	function getContent($xpath="/") {
		if ($xpath=="/") return $this->getAll();
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$selectedXml="";
		foreach($xpathResult->nodeset as $node) {
			// est ce un attribut ?
			if (get_class($node)=="domattribute") {
				$content=$node->value();
				//$content=$node->dump_mem(true,$this->dom->encoding);
			} else { // il s'agit bien d'un noeud
				//$content=$this->_dump_node_with_encoding($node,$this->dom->encoding);
				$content=$node->get_content();
			}
			$selectedXml.=$content;
		}
		return $selectedXml;
	}
	// retourne le contnenu des noeuds sous forme de tableau
	function getContentAsArray($xpath="/") {
		if ($xpath=="/") return $this->getAll();
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$result=array();
		foreach($xpathResult->nodeset as $node) {
			// est ce un attribut ?
			if (get_class($node)=="domattribute") {
				$content=$node->value();
				//$content=$node->dump_mem(true,$this->dom->encoding);
			} else { // il s'agit bien d'un noeud
				//$content=$this->_dump_node_with_encoding($node,$this->dom->encoding);
				$content=$node->get_content();
			}
			$result[]=$content;
		}
		return $result;
	}
	// retourne en tableau les attributs du ou des noeuds sélectionnés
	function getAttributes($xpath="/") {
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// construction de la chaine a retourner
		$result=array();
		foreach($xpathResult->nodeset as $node) {
			// le noaud a t il des attributs ?
			//if (!$node->has_attributes) continue;
			// recup des attributs
			if (!$xpathResult2=$this->_getXpathResult($xpath."/attribute::*")) continue;
			foreach ($xpathResult2->nodeset as $attribute) {
				$result[$attribute->name()]=$attribute->value();
			}
		}
		return $result;
	}
	
	// insere une nouvelle chaine xml a l'emplacement indique. Si un contenu 
	// est deja present, l'ajout se fera a sa suite.
	function insert($xpath="/",$xml="",$encodeEntities=false) {
		// reperage des emplacements a bouturer avec xpath
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// conversion de la chaine a inserer en noeud domxml
		/*echo*/ $xml=$this->_applyToInput($xml);
		// suppression de la declaration xml
		$xml=$this->_wipeXmlDeclaration($xml);
		if (!$greffe=$this->_convertToNode($xml)) return false;
		// parcours des emplacements
		for($f=0; $f<count($xpathResult->nodeset); $f++) {
			// recup de l'emplacement en tant que noeud
			$branch=$xpathResult->nodeset[$f];
			// greffe du nouveau noeud
			if (!$branch->append_child($greffe)) {
				$this->_error("couldn't append new node",E_USER_NOTICE);
				return false;
			}
		}
		return true;
	}
	// insere une nouvelle chaine xml a l'emplacement indique. Si un contenu 
	// est deja present, l'ajout se fera au debut
	function insertFirst($xpath="/",$xml="",$encodeEntities=false) {
		// reperage des emplacements a bouturer avec xpath
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// conversion de la chaine a inserer en noeud domxml
		/*echo*/ $xml=$this->_applyToInput($xml);
		// suppression de la declaration xml
		$xml=$this->_wipeXmlDeclaration($xml);
		if (!$greffe=$this->_convertToNode($xml)) return false;
		// parcours des emplacements
		for($f=0; $f<count($xpathResult->nodeset); $f++) {
			// recup de l'emplacement en tant que noeud
			$branch=$xpathResult->nodeset[$f];
			// recherche du premier fils existant
			if ($firstChild=$branch->first_child()) {
				if (!$branch->insert_before($greffe, $firstChild)) {
					$this->_error("couldn't append new node",E_USER_NOTICE);
					return false;
				}
			// greffe du nouveau noeud
			} else if (!$branch->append_child($greffe)) {
				$this->_error("couldn't append new node",E_USER_NOTICE);
				return false;
			}
		}
		return true;
	}
	// ajoute un attribut et sa valeur au(x) noeud(s) selectionne(s)
	function addAttribute() {
		echo "a coder";
	}
	// remplace un noeud par un autre
	function replace($xpath="/",$xml="") {
		$xml=$this->_applyToInput($xml);
		$this->delete($xpath);
		echo "ici on doit retoruver le xpath du niveau superieur, a coder";
		return $this->insert($xpath,$xml);
	}
	// remplace le contenu d'un noeud
	function replaceContent($xpath="/",$xml="") {
		$xml=$this->_applyToInput($xml);
		$this->deleteContent($xpath);
		return $this->insert($xpath,$xml);	
	}
	// elague le document xml des emplacements selectionnes par le xpath
	function delete($xpath="/") {
		// reperage des branches a couper avec xpath
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// parcours des noeuds selectionnes
		for ($f=0; $f<count($xpathResult->nodeset); $f++) {
			// recup de l'emplacement en tant que noeud
			$branch=$xpathResult->nodeset[$f];
			// dereferencement du noeud
			$branch->unlink_node();
		}
		return true;
	}
	// vide le(s) noeud(s) de son(leur) contenu
	function deleteContent($xpath="/") {
		// reperage de la selection avec xpath
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		// parcours des noeuds selectionnes
		for ($f=0; $f<count($xpathResult->nodeset); $f++) {
			// recup de l'emplacement en tant que noeud
			$branch=$xpathResult->nodeset[$f];
			// selection des fils
			$children=$branch->child_nodes();
			for (reset($children); $child=&current($children); next($children)) {
				$child->unlink_node();
			}
			//echo affvar($children);
		}
		return true;
	}
	// retourne le nom du noeud racine
	function getRootName() {
		$rootNode=$this->dom->document_element();
		return $rootNode->tagname();
	}
	// donne au tag la valeur, qu'il existe ou non
	function set($xpath,$value) {
		//echo $xpath." => ".$value;
		if ($this->get($xpath)) {
			//echo "toto";
			$this->deleteContent($xpath);
			return $this->insert($xpath,$value);
		} else {
			//echo "tata";
			return $this->insert($xpath,$value);
		}
	}
	
	function setAttribute($xpath,$name,$value) {
      // reperage des noeuds selectionnes
      if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		foreach ($xpathResult->nodeset as $node) {
			if (!$node->set_attribute($name,$value)) return false;
		}	
	}
	// renvoie le nombre de noeuds correspondants au xpath
	function count($xpath="/") {
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return 0;
		if (!is_array($xpathResult->nodeset)) return 0;
		return count($xpathResult->nodeset);
	}
	// renvoie le nom du tag selectionné	
	function getTagName($xpath="/") {
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		if (count($xpathResult->nodeset)!=1) 
			$this->_notice("getTagName : plus d'un noeud selectionn&eacute;");
		$node=$xpathResult->nodeset[0];
		return $node->tagname();
	}
	// modifie le nom des tags selectionn?
	function rename($xpath="/", $newName) {
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		foreach ($xpathResult->nodeset as $node) {
			if (!$node->set_name($newName)) return false;
		}
		return true;
	}
	// applique une fonction au contenu des noeuds s?ctionn?
	function applyFunction($xpath="/",$function) {
		if (!function_exists($function)) $this->_error("applyFunction : la fonction n'existe pas'");
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		foreach ($xpathResult->nodeset as $node) {
			// le texte est consid? comme un noeud FILS du noeud...
			$child_node=$node->first_child();
			if (!$content=$child_node->get_content()) $this->_notice("noeud sans contenu ?");
			$child_node->unlink();
			$newContent=$function($content);
			if (!$node->set_content($newContent)) return false;
		}
		return true;	
	}
	// applique une methode au contenu TEXTE des noeuds sélectionnés
	function applyMethod($xpath="/", $method) {
		//if (!function_exists($function)) $this->_error("applyFunction : la fonction n'existe pas'");
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		foreach ($xpathResult->nodeset as $node) {
			// le texte est consid? comme un noeud FILS du noeud...
			$child_node=$node->first_child();
			if (!$content=$child_node->get_content()) $this->_notice("noeud sans contenu ?");
			$child_node->unlink();
			$newContent=$this->$method($content);
			if (!$node->set_content($newContent)) return false;
		}
		return true;
	}
	
	// renvoie l'arbre xml sous forme de tableau php multidimensionnel
	function getAsTree($xpath=NULL) {
		//echo "<p>$xpath</p>";
		// analyse du noeud racine
		if ($xpath===NULL) {
			$node=$this->dom->document_element();
			$tree=$this->getNodeAsTree($node);
			return $tree;
		}
		// reperage des noeuds selectionnes
		if (!$xpathResult=$this->_getXpathResult($xpath)) return false;
		/*if (count($xpathResult->nodeset)==1) {
			return $this->getNodeAsTree(current($xpathResult->nodeset));
		}*/
		$tree=array();
		foreach ($xpathResult->nodeset as $node) {
			$tree[]=$this->getNodeAsTree($node);
		}
		//echo "<pre>".print_r($tree)."</pre>";
		return $tree;
	}
	function getNodeAsTree($node=NULL) {
		//echo "toto";
		// reperage des noeuds selectionnes
		if ($node===NULL) $node=$this->dom->document_element();
		//echo $node->node_type();
		if ($node->node_type()!=XML_ELEMENT_NODE) return false;
		$tree=array();
		//$type=$node->node_type();
		$name=$node->tagname();
		$tree['name()']=$name;
		// attributs
		if ($node->has_attributes()) {
			foreach ($node->attributes() as $attr) {
				//echo affvar($attr,'attr',false);
				$tree['attributes()'][$attr->name()]=$attr->value();
			}
		}
		// elements enfants
		foreach ($node->child_nodes() as $child) {
			//echo affvar($child);
			//$name=$child->name();
			switch ($child->node_type()) {
				case XML_ELEMENT_NODE:
					$name=$child->tagname();
					$tree['childs()'][]=$this->getNodeAsTree($child);
					break;
				case XML_TEXT_NODE:
					//$tree[$child->tagname][]=$node->get_content();
					//$name=
					$content=$child->get_content();//cho affvar($content,'content for '.$child->name(),false);
					if (trim($content)=='') break;
					//echo affvar($child,'text node',false);
					$tree['text()']=trim($content);
					break;
				default:
					//echo "unkown node type ".affvar($child);
					//echo affvar($child);
			}
		}
		return $tree;
	}
	
	function setAuthorizedCharset($charset) {
		$this->authorizedCharset=$charset;
	}
	
	function setEncoding($encoding) {
		$this->encoding=$encoding;
	}
/*---------------------------------------------------------------------------
                                                           proprietes privees
---------------------------------------------------------------------------*/
	var $dom; // representation interne du document xml
	var $log; // liste des erreurs et actions
/*---------------------------------------------------------------------------
                                                             methodes privees
---------------------------------------------------------------------------*/
	// tente de creer un objet dom avec la chaine xml
	function _makeDom($xml) {
		// chargement en memoire
		$error=array();
		//$dom=@domxml_open_mem($xml);
		$dom=domxml_open_mem($xml,DOMXML_LOAD_PARSING,$error);
		if (!$dom) {
			if (!is_array($error)) return false;
			foreach ($error as $errorline) {    # Loop through all errors
				$txt=$errorline['errormessage']
				//." Node  : " . $errorline['nodename'] . "\n"
				." Line  : " . $errorline['line'] . "\n"
				." Column : " . $errorline['col'] . "\n\n";
				//$lines=explode('\l',$xml); echo affvar($lines);
				//echo "<pre>".htmlentities($xml)."</pre>";
				//echo htmlentities(substr($lines[$errorline['line']-1], $errorline['col']-20, 40));
				trigger_error($txt,E_USER_NOTICE);
			}
			return false;
		}
		return $dom;
	}
	
	function _findEncoding($xml) {
		$found=array();
		if (!eregi('encoding="((ISO-8859-15)|(UTF-8))"',$xml,$found)) return false;
		$encoding=$found[1]; //echo affvar($encoding);
		return $encoding;
	}
	// cette fonction est appliquee a tout chaine importee dans le xml
	function _applyToInput($string) {
		// encodage utf-8 si necessaire
		if ($this->encoding=="ISO-8859-15") $string=$this->_ifUtf8ThenIso($string);
		else if ($this->encoding=="UTF-8") $string=$this->_ifIsoThenUtf8($string);
		//$string=$this->_wipeUnauthorizedChar($string);
		return $string;
	}
	
	// decode du utf-8 en ISO-8859-15 si necessaire
	function _ifUtf8ThenIso($string) {
		if ($this->_seemsUtf8($string)) {
			//$this->_notice("utf-8 !");
			//$string=$this->_utf8ToUnicodeEntities($string);
			$string=$this->_cleanUtf8Decode($string);
			$string=$this->_replaceEncodingDeclaration($string,"ISO-8859-15");
			//echo htmlentities($string);
		}
	   return $string;	
	}
	
	function _ifIsoThenUtf8($string) {
		if (!$this->_seemsUtf8($string)) {
			$string=$this->_cleanUtf8Encode($string);
			$string=$this->_replaceEncodingDeclaration($string,"UTF-8");
		}
	   return $string;	
	}
	
   // renvoie le contenu texte d'un noeud en prenant en compte l'encodage
   function _dump_node_with_encoding($node,$encoding=NULL){
		if (!$encoding) $encoding=$this->encoding;
      $domNode = domxml_new_doc("1.0");
      $clonedNode = $node->clone_node(true);
      //if ($clonedNode->is_blank_node()) return "";
      $domNode->append_child($clonedNode);
      $result = $domNode->dump_mem(true,$encoding);
      $pos = strpos($result,"?".">");
      return substr($result,$pos+2);
   }
   
   // renvoie un objet xpath
   function _getXpathResult($xpath) {
      if (!$xpathResult=&xpath_eval_expression($this->xpathContext, $xpath)) {
			// erreur de xpath_eval_expression
			$this->_notice("couldn't locate xpath ($xpath)",E_USER_NOTICE);
			return false;
		}
		if (count($xpathResult->nodeset)==0) {
			// le xpath ne correspond a aucun noeud
			//$this->_error("aucun noeud selectionne",E_USER_NOTICE);
			return false;
		}
		return $xpathResult;
	}
	
	// retourne la chaine en noeud pret a etre insere UTF-8 ISO-8859-1
	function _convertToNode($xmlString,$encoding=NULL) {
		if (!$encoding) $encoding=$this->encoding;
		// creation d'un objet domdocument
		if ($newdoc=$this->_makeDom("<?xml version=\"1.0\" encoding=\"$encoding\"?".">"
			.$xmlString) and $greffon=$newdoc->document_element()) {
			//echo affvar($newdoc);
			// recuperation de la racine comme objet domnode
			//$greffon=$newdoc->document_element();
		/*} else if ($newdoc=@domxml_open_mem(
		// ajout racine root
		"<?xml version=\"1.0\" encoding=\"$encoding\"?".">"
		."<root>".$xmlString."</root>")) {
			$greffon=$newdoc->document_element();*/
		} else if ($greffon=$this->dom->create_text_node($xmlString)) {
			// pas de noeud racine ?
			// probleme d'encodage
			//$newdoc=domxml_new_doc("1.0");
			//$greffon=$newdoc->create_text_node($xmlString);
			//echo $xmlString;
			//echo affvar($greffon);
			//$greffon=$this->dom->create_text_node($xmlString);
			//$greffon=$newdoc->document_element();
			/*$this->_error("contenu texte ? probleme d'encodage ? ".htmlentities($xmlString)
			,E_USER_NOTICE);*/
		} else {
			return false;
		}
		//echo affvar($greffon);
      // clonage du noeud pour manip hors document d'origine
      $greffe=$greffon->clone_node(true);
      return $greffe;
	}
	
	// determine si une chaine est encodee en utf-8
   function _seemsUtf8($Str) {
      for ($i=0; $i<strlen($Str); $i++) {
         if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
         elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
         elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
         elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
         elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
         elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
         else return false; # Does not match any model
            for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
            if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
            return false;
         }
      }
      return true;
   }
	// decode "proprement" une chaine utf-8
	function _cleanUtf8Decode($in_str) {
   	// Replace ? with a unique string
   	$new_str = str_replace("?", "q0u0e0s0t0i0o0n", $in_str);
   	// Try the utf8_decode
   	$new_str=utf8_decode($new_str);
		$new_str=str_replace('?','',$new_str);
		$new_str = str_replace("q0u0e0s0t0i0o0n", "?", $new_str);
   	return $new_str;
	}
	function _cleanUtf8Encode($in_str) {
   	// Replace ? with a unique string
   	$new_str = str_replace("?", "q0u0e0s0t0i0o0n", $in_str);
   	// Try the utf8_decode
   	$new_str=utf8_encode($new_str);
		$new_str=str_replace('?',' * ',$new_str);
		$new_str = str_replace("q0u0e0s0t0i0o0n", "?", $new_str);
   	return $new_str;
	}
	
// encodage utf8 => unicode
function _utf8ToUnicodeEntities ($source) {
   // array used to figure what number to decrement from character order value
   // according to number of characters used to map unicode to ascii by utf-8
   $decrement[4] = 240;
   $decrement[3] = 224;
   $decrement[2] = 192;
   $decrement[1] = 0;
  
   // the number of bits to shift each charNum by
   $shift[1][0] = 0;
   $shift[2][0] = 6;
   $shift[2][1] = 0;
   $shift[3][0] = 12;
   $shift[3][1] = 6;
   $shift[3][2] = 0;
   $shift[4][0] = 18;
   $shift[4][1] = 12;
   $shift[4][2] = 6;
   $shift[4][3] = 0;
  
  $pos = 0;
   $len = strlen ($source);
   $encodedString = '';
   while ($pos < $len) {
       $asciiPos = ord (substr ($source, $pos, 1));
       if (($asciiPos >= 240) && ($asciiPos <= 255)) {
           // 4 chars representing one unicode character
           $thisLetter = substr ($source, $pos, 4);
           $pos += 4;
       }
       else if (($asciiPos >= 224) && ($asciiPos <= 239)) {
           // 3 chars representing one unicode character
           $thisLetter = substr ($source, $pos, 3);
           $pos += 3;
       }
       else if (($asciiPos >= 192) && ($asciiPos <= 223)) {
           // 2 chars representing one unicode character
           $thisLetter = substr ($source, $pos, 2);
           $pos += 2;
       }
       else {
           // 1 char (lower ascii)
           $thisLetter = substr ($source, $pos, 1);
           $pos += 1;
       }
       // process the string representing the letter to a unicode entity
       $thisLen = strlen ($thisLetter);
       $thisPos = 0;
       $decimalCode = 0;
       while ($thisPos < $thisLen) {
           $thisCharOrd = ord (substr ($thisLetter, $thisPos, 1));
           if ($thisPos == 0) {
               $charNum = intval ($thisCharOrd - $decrement[$thisLen]);
               $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
           }
           else {
               $charNum = intval ($thisCharOrd - 128);
               $decimalCode += ($charNum << $shift[$thisLen][$thisPos]);
           }
           $thisPos++;
       }
       if ($thisLen == 1)
           $encodedLetter = "&#". str_pad($decimalCode, 3, "0", STR_PAD_LEFT) . ';';
       else
           $encodedLetter = "&#". str_pad($decimalCode, 5, "0", STR_PAD_LEFT) . ';';
       $encodedString .= $encodedLetter;
   }
   return $encodedString;
}
	// change la declaration d'encodage
	function _replaceEncodingDeclaration($xml,$new="ISO-8859-15") {
		if (strpos($xml,'<?xml')!==false) {
   		$endPos=strpos($xml,'?'.'>');
   		$debut="<?xml version=\"1.0\" encoding=\"$new\"?".">";
   		$xml=$debut.substr($xml,$endPos+2);
		}
		$this->encoding=$new;
		return $xml;
	}
	
	function _wipeXmlDeclaration($xml) {
		if (strpos($xml,'<?xml')!==false) {
   		$endPos=strpos($xml,'?'.'>');
   		$xml=substr($xml,$endPos+2);
		}
		return $xml;
	}
	
	// supprime tout caractere hors de this->authorizedCharset
	function _wipeUnauthorizedChar($string) {
		$newString="";
		for ($f=0; $f<strlen($string); $f++) {
			if (strpos($this->authorizedCharset,$string[$f])!==false)
				$newString.=$string[$f];
		}
		return $newString;
	}
	// traite les erreurs
	function _error($txt) {
		trigger_error("classe ".get_class($this).": ".$txt,E_USER_ERROR);
	}
	function _warning($txt) {
		trigger_error("classe ".get_class($this).": ".$txt,E_USER_WARNING);
	}
	
	function _notice($txt) {
	   trigger_error("classe ".get_class($this).": ".$txt,E_USER_NOTICE);
	   return true;
	}	
/*---------------------------------------------------------------------------
                                                             fin de la classe
---------------------------------------------------------------------------*/
}
/*---------------------------------------------------------------------------
																					usage de xpath
<?xml version=\"1.0\" encoding=\"ISO-8859-15\"? >
    <collection>
		<info>ici une info</info>
        <livre nom=\"20000 lieues sous les mer\">
            <resume>un livre super bien avec un sous marin</resume>
        </livre>
       <livre nom=\"L'?anger\">
            <resume>un livre super bien avec un ?anger</resume>
        </livre>
        <livre nom=\"ma vie\">
            <resume>un livre super chiant sur ma vie</resume>
            <info nom="prix" valeur="tres cher" />
        </livre>
       <livre>
				<resume>un livre dont la couverture est arrachee</resume>
        </livre>
    </collection>
collection/info
chemin absolu vers le noeud info
//info
tous les noeuds resume
/collection/livre/*
tous les noeuds contenus dans le debut du chemin

/collection/livre[1] = /collection/livre[first()]
le premier item

/collection/livre[last()]
le dernier item
//@nom
tous les attributs nom

//livre[@nom]
les livres qui ont un attribut nom

//*[@nom]
tous les tags qui ont un attribut nom

//*[@*]
tous les elemnts qui ont au moins un attribut

//*[not(@*)]

tous les elements qui n'ont aucun attribut

//*[@nom='ma vie']
tous les elements qui ont pour attribut nom avec pour valeur 'ma vie'
---------------------------------------------------------------------------*/
?>
